﻿namespace Xmart.Expressions.DynamicPredicate;

public class DynamicPredicateModel
{
    /// <summary>
    /// 排序号
    /// </summary>
    public int Index { get; set; }
    /// <summary>
    /// 模型类型 1.条件项 2.条件组
    /// </summary>
    public DynamicPredicateType Type { get; set; }
    /// <summary>
    /// 模型组关系 1.且 2.或，条件项时该字段无意义
    /// </summary>
    public DynamicPredicateRelation Relation { get; set; }
    /// <summary>
    /// 条件项，Type=1时读取该值
    /// </summary>
    public DynamicPredicateItem? Predicate { get; set; }
    /// <summary>
    /// 条件组，Type=2时读取该值
    /// </summary>
    public List<DynamicPredicateModel>? Group { get; set; }
    /// <summary>
    /// 整理
    /// </summary>
    /// <param name="isDeep">是否深度整理</param>
    /// <returns></returns>
    public DynamicPredicateModel Defrag(bool isDeep = false)
    {
        if (Type == DynamicPredicateType.Predicate)
        {
            return this;
        }

        //找出聚合组
        var aggergates = Group
            .Where(r => r.Predicate != null && !string.IsNullOrWhiteSpace(r.Predicate.AggregateGroupName));

        if (!isDeep && !aggergates.Any())
        {
            return this;
        }

        //从聚合组中排除，剩余的为集合分组数据的筛选条件
        var group = Group.Where(r => !aggergates.Contains(r))
            .Select(r => isDeep ? r.Defrag(isDeep) : r)
            .ToList();
        if (aggergates.Any())
        {
            //聚合组归类
            var aggergateGroups = aggergates.GroupBy(r => r.Predicate!.AggregateGroupName);

            var index = aggergates.Max(r => r.Index) + 1;

            foreach (var item in aggergateGroups)
            {
                var propertyName = item.First().Predicate!.Property;
                var aggActions = item.Where(r => r.Predicate!.AggregateType.HasValue);
                var agg = item.Count() == 1 && aggActions.Any() ? null : new DynamicPredicateModel
                {
                    Index = 0,
                    Type = DynamicPredicateType.Group,
                    Relation = Relation,
                    Group = new(item.Where(r => !r.Predicate!.AggregateType.HasValue)
                        .Select(r => new DynamicPredicateModel()
                        {
                            Index = r.Index,
                            Type = r.Type,
                            Relation = r.Relation,
                            Group = r.Group,
                            Predicate = new()
                            {
                                Property = r.Predicate!.AggregateProperty,
                                CompareType = r.Predicate!.CompareType,
                                CustomCompareType = r.Predicate!.CustomCompareType,
                                ItemPredicateModel = r.Predicate!.ItemPredicateModel,
                                Value = r.Predicate!.Value,
                            }
                        })
                    )
                };
                if (agg != null)
                {
                    group.Add(new()
                    {
                        Index = index,
                        Type = DynamicPredicateType.Predicate,
                        Predicate = new()
                        {
                            Property = propertyName,
                            CompareType = CompareType.ItemAny,
                            ItemPredicateModel = agg,
                        }
                    });
                }
                if (aggActions.Any())
                {
                    foreach (var aggAction in aggActions)
                    {
                        var m = aggAction.Clone();
                        if (agg != null)
                        {
                            m.Predicate!.ItemPredicateModel = agg;
                        }
                        m.Index = index + 1;
                        group.Add(m);
                    }
                }
                index += 2;
            }
        }

        return new()
        {
            Index = Index,
            Type = Type,
            Group = group,
            Relation = Relation,
        };
    }
    /// <summary>
    /// 是否包含有属性的条件项
    /// </summary>
    /// <param name="property">属性</param>
    /// <returns></returns>
    public bool Contains(string property)
    {
        return Contains(r => r.Predicate?.Property == property);
    }
    /// <summary>
    /// 是否存在满足条件的条件项
    /// </summary>
    /// <param name="predicate">条件</param>
    /// <returns></returns>
    public bool Contains(Func<DynamicPredicateModel, bool> predicate)
    {
        if (Type == DynamicPredicateType.Predicate)
        {
            return predicate(this);
        }
        else
        {
            if (Group != null)
            {
                foreach (var item in Group)
                {
                    if (item.Contains(predicate))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
    /// <summary>
    /// 获取包含有属性的条件项的模型
    /// </summary>
    /// <param name="property">属性</param>
    /// <returns></returns>
    public List<DynamicPredicateModel> GetPropertyModels(string property)
    {
        var items = new List<DynamicPredicateModel>();
        ForEach(item =>
        {
            if (item.Predicate?.Property == property)
            {
                items.Add(item);
            }
        });
        return items;
    }
    /// <summary>
    /// 遍历条件项的模型
    /// </summary>
    /// <param name="action">遍历执行动作</param>
    public void ForEach(Action<DynamicPredicateModel> action)
    {
        if (Type == DynamicPredicateType.Predicate)
        {
            if (Predicate != null)
            {
                action(this);
                Predicate.ItemPredicateModel?.ForEach(action);
            }
        }
        else
        {
            if (Group != null)
            {
                foreach (var item in Group)
                {
                    item.ForEach(action);
                }
            }
        }
    }
    /// <summary>
    /// 替换条件项
    /// </summary>
    /// <param name="property">属性</param>
    /// <param name="replacement">替换操作</param>
    /// <returns></returns>
    public DynamicPredicateModel Replace(
        string property,
        Func<DynamicPredicateItem, DynamicPredicateItem> replacement)
    {
        if (Contains(property))
        {
            var clone = Clone();
            var models = GetPropertyModels(property);
            foreach (var model in models)
            {
                model.Predicate = replacement(model.Predicate!);
            }
            return clone;
        }
        return this;
    }
    /// <summary>
    /// 替换条件项的模型
    /// </summary>
    /// <param name="property">属性</param>
    /// <param name="replacement">替换操作</param>
    /// <returns></returns>
    public DynamicPredicateModel Replace(string property, Action<DynamicPredicateModel> replacement)
    {
        if (Contains(property))
        {
            var clone = Clone();
            var models = GetPropertyModels(property);
            foreach (var model in models)
            {
               replacement(model);
            }
            return clone;
        }
        return this;
    }
    /// <summary>
    /// 深克隆
    /// </summary>
    /// <returns></returns>
    public DynamicPredicateModel Clone()
    {
        var clone = new DynamicPredicateModel
        {
            Index = Index,
            Type = Type,
            Relation = Relation,
        };
        if (Predicate != null)
        {
            clone.Predicate = new DynamicPredicateItem
            {
                Property = Predicate.Property,
                CompareType = Predicate.CompareType,
                CustomCompareType = Predicate.CustomCompareType,
                ItemPredicateModel = Predicate.ItemPredicateModel?.Clone(),
                Value = Predicate.Value,
                AggregateProperty = Predicate.AggregateProperty,
                AggregateType = Predicate.AggregateType,
            };
        }
        if (Group != null)
        {
            clone.Group = new();
            foreach (var item in Group)
            {
                clone.Group.Add(item.Clone());
            }
        }
        
        return clone;
    }
}
